package com.google.code.mergeinformer.service.impl;

import com.google.code.mergeinformer.model.Branch;
import com.google.code.mergeinformer.model.Project;
import com.google.code.mergeinformer.model.Task;
import com.google.code.mergeinformer.service.ProjectService;
import com.google.code.mergeinformer.service.TaskService;
import com.google.code.mergeinformer.service.VcsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.NoResultException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;

/**
 * @author Dmitry Golubev
 */
@Service
public class ProjectServiceImpl extends ServiceBaseImpl<Project> implements ProjectService, ApplicationListener<ContextRefreshedEvent> {

    private volatile boolean initialized = false;

    @Autowired
    private VcsService vcsService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private ExecutorService executorService;

    @Override
    @Transactional(readOnly = true)
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (!initialized) {
            for (Project project : getAllProjects()) {
                schedule(project);
            }
            initialized = true;
        }
    }

    @Override
    public List<Project> getAllProjects() {
        return em.createNamedQuery("Project.getAll", Project.class).getResultList();
    }

    @Override
    public Project getByShortName(String shortName) {
        try {
            return em.createNamedQuery("Project.byShortName", Project.class)
                    .setParameter("shortName", shortName).getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }

    @Override
    @Transactional
    public Project refreshBranches(Project project) {
        final List<Branch> branches;
        try {
            branches = vcsService.discoverBranches(project);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        project.replaceBranches(branches);
        persist(project);
        return project;
    }

    @Override
    public boolean isNameExists(String name) {
        return em.createNamedQuery("Project.isNameBusy", Long.class).setParameter("name", name).getSingleResult() == 1;
    }

    @Override
    public boolean isShortNameExists(String shortName) {
        return em.createNamedQuery("Project.isShortNameBusy", Long.class).setParameter("shortName", shortName).getSingleResult() == 1;
    }

    @Override
    public void schedule(Project project) {
        for (Task task : project.getTasks()) {
            taskService.schedule(task);
        }
    }

    @Override
    public void execute(final Project project) {
        final List<Callable<Void>> taskRunners = new ArrayList<>(project.getTasks().size());
        for (final Task task : project.getTasks()) {
            taskRunners.add(new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    final Task executedTask = taskService.execute(task);
                    project.addTask(executedTask);
                    return null;
                }
            });
        }
        try {
            executorService.invokeAll(taskRunners);
        } catch (InterruptedException ignored) {
        }
    }
}
